package com.joysuch.wwyt.mh.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.zlg.common.util.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Splitter;
import com.joysuch.wwyt.bp.constant.NotifyContents;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.common.entity.CommonIndexNotify;
import com.joysuch.wwyt.common.enums.AppBusinessNoticeTypes;
import com.joysuch.wwyt.common.repository.CommonIndexNotifyDao;
import com.joysuch.wwyt.common.service.CommonIndexNotifyService;
import com.joysuch.wwyt.core.bean.SimpleIdMapping;
import com.joysuch.wwyt.core.bean.SimpleSelectListBean;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.DateTimeUtil;
import com.joysuch.wwyt.core.common.util.JsonUtil;
import com.joysuch.wwyt.core.common.util.SimpleDateFormatCache;
import com.joysuch.wwyt.core.entity.BaseUser;
import com.joysuch.wwyt.core.enums.DeleteFlag;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.mh.bean.MhMajorHazardTaskSearchBean;
import com.joysuch.wwyt.mh.bean.vo.MhMajorHazardPlanItemVO;
import com.joysuch.wwyt.mh.bean.vo.MhMajorHazardTaskPageVO;
import com.joysuch.wwyt.mh.bean.vo.MhMajorHazardTaskVO;
import com.joysuch.wwyt.mh.bo.MhMajorHazardTaskAssignBO;
import com.joysuch.wwyt.mh.bo.MhMajorHazardTaskPageBO;
import com.joysuch.wwyt.mh.constant.TaskDeadLineDesc;
import com.joysuch.wwyt.mh.dto.MhMajorHazardTaskRecordDTO;
import com.joysuch.wwyt.mh.entity.*;
import com.joysuch.wwyt.mh.enums.MhMajorHazardCheckUserEnum;
import com.joysuch.wwyt.mh.enums.PlanCycleType;
import com.joysuch.wwyt.mh.enums.PlanLoopType;
import com.joysuch.wwyt.mh.enums.TaskStatus;
import com.joysuch.wwyt.mh.mapper.MhMajorHazardPlanItemMapper;
import com.joysuch.wwyt.mh.mapper.MhMajorHazardTaskAssignMapper;
import com.joysuch.wwyt.mh.mapper.MhMajorHazardTaskMapper;
import com.joysuch.wwyt.mh.repository.MhMajorHazardDao;
import com.joysuch.wwyt.mh.service.*;
import com.joysuch.wwyt.mh.utils.DigitalConversionUtil;
import com.joysuch.wwyt.mh.utils.TaskCycleUtil;
import com.joysuch.wwyt.mh.utils.WeekRange;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zqw
 * @since 2022-05-24
 */
@Service
@Slf4j
public class MhMajorHazardTaskServiceImpl extends ServiceImpl<MhMajorHazardTaskMapper, MhMajorHazardTask> implements MhMajorHazardTaskService {
    @Autowired
    private MhMajorHazardTaskMapper mhMajorHazardTaskMapper;
    @Autowired
    private MhMajorHazardPlanService mhMajorHazardPlanService;
    @Autowired
    private MhMajorHazardDao mhMajorHazardDao;
    @Autowired
    private MhMajorHazardTaskAssignService mhMajorHazardTaskAssignService;
    @Autowired
    private MhMajorHazardPlanItemService mhMajorHazardPlanItemService;
    @Autowired
    private MhMajorHazardTaskRecordService mhMajorHazardTaskRecordService;
    @Autowired
    private MhMajorHazardTaskAssignMapper assignMapper;
    @Autowired
    private MhMajorHazardTaskAssignService assignService;
    @Autowired
    private MhMajorHazardTaskRecordItemService mhMajorHazardTaskRecordItemService;
    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private MhMajorHazardPlanItemMapper mhMajorHazardPlanItemMapper;
    @Autowired
    private MhMajorHazardTaskRecordAssignService mhMajorHazardTaskRecordAssignService;
    @Autowired
    private MhMajorHazardTaskAssignMapper mhMajorHazardTaskAssignMapper;
    @Autowired
    private CommonIndexNotifyService indexNotifyService;
    @Autowired
    private CommonIndexNotifyDao commonIndexNotifyDao;

    @Override
    public ResultBean pageTask(MhMajorHazardTaskSearchBean searchBean) {
        Long currentUserId = null;
        // 非超级管理员只能看到自己的任务
        if (!Context.getCurrentUser().isSuperAdmin()) {
            currentUserId = Context.getCurrentUserId();
        }
        String orgCode = Context.getCompanyCode();
        Long tenentId = Context.getCurrentTenantId();
        Page<MhMajorHazardTaskPageBO> page = new Page<>(searchBean.getPage() + 1, searchBean.getSize());
        Page<MhMajorHazardTaskPageBO> pageData = mhMajorHazardTaskMapper.selectByPage(page, currentUserId, orgCode, tenentId, searchBean);
        return ResultBean.pageData(convertTaskPageData(pageData.getRecords(), searchBean.getReqBy(), currentUserId), pageData.getTotal());
    }

    //组装危险源任务分页数据
    private List<MhMajorHazardTaskPageVO> convertTaskPageData(List<MhMajorHazardTaskPageBO> sourceData, String reqBy, Long currentUserId) {
        List<MhMajorHazardTaskPageVO> voList = new ArrayList<>();
        Date date = new Date();
        for (MhMajorHazardTaskPageBO bo : sourceData) {
            MhMajorHazardTaskPageVO vo = new MhMajorHazardTaskPageVO();
            BeanUtils.copyProperties(bo, vo);
            vo.setTaskId(bo.getId());
            vo.setId(bo.getAssignId());
            //检查人员
            String checkUserName = baseUserDao.getRealNameById(bo.getAssignUser());
            vo.setCheckUserName(checkUserName);
            //危险源
            Optional<MhMajorHazard> omh = mhMajorHazardDao.findById(bo.getMhId());
            if (omh.isPresent()) {
                vo.setMhName(omh.get().getName());
            }
            //任务开始时间
            Date startDate = new Date(bo.getTaskStartTime() * 1000);
            Date endDate = new Date(bo.getTaskEndTime() * 1000);
            vo.setTaskStratTime(startDate);
            // 履职 删除按钮权限
//            if (bo.getFormulateDepartId() != null && !CollectionUtils.isEmpty(authDepartIds) && authDepartIds.contains(bo.getFormulateDepartId())) {
//                vo.setDeleteButtonFlag(true);
//            }
            if ((currentUserId == null || (bo.getAssignUser() != null && bo.getAssignUser().equals(currentUserId))) && !date.after(endDate) && !date.before(startDate)) {
                vo.setJobButtonFlag(true);
            }
            voList.add(vo);
        }
        return voList;
    }

    private String getTaskUserName(Long taskId, Integer checkUserType) {
        String nameStr = "";
        List<MhMajorHazardTaskAssign> assignList = mhMajorHazardTaskAssignMapper.findByTaskId(taskId);
        if (CollectionUtils.isNotEmpty(assignList) && checkUserType != null) {
            List<Long> userIds = assignList.stream().map(MhMajorHazardTaskAssign::getAssignUser).distinct().collect(Collectors.toList());
            List<BaseUser> users = baseUserDao.getAllByIds(userIds);
            String type = MhMajorHazardCheckUserEnum.getNameByCode(checkUserType);
            String userNameStr = users.stream().map(BaseUser::getRealName).collect(Collectors.joining(Constant.SPLIT_COMMA));
            nameStr = userNameStr + "(" + type + ")";
        }
        return nameStr;
    }

    @Override
    public MhMajorHazardTaskVO findById(Long assignId) {
        MhMajorHazardTaskVO vo = new MhMajorHazardTaskVO();
        MhMajorHazardTaskAssign taskAssign = mhMajorHazardTaskAssignService.getById(assignId);
        if (taskAssign == null) {
            return vo;
        }
        MhMajorHazardTask task = getById(taskAssign.getTaskId());
        if (task == null) {
            return vo;
        }
        MhMajorHazardPlan plan = mhMajorHazardPlanService.getById(task.getPlanId());
        if (plan == null) {
            return vo;
        }
        BeanUtils.copyProperties(task, vo);
        vo.setReminderDays(plan.getReminderDays());
        vo.setRemark(plan.getRemark());
        //随行人员
        List<SimpleSelectListBean> entourageIds = new ArrayList<>();
        if (StringUtils.isNotBlank(task.getEntourageIds())) {
            List<Long> userIds = Arrays.asList(task.getEntourageIds().split(",")).stream().map(s -> Long.valueOf(s)).collect(Collectors.toList());
            List<BaseUser> users = baseUserDao.getAllByIds(userIds);
            if (!CollectionUtils.isEmpty(users)) {
                users.forEach(u -> {
                    SimpleSelectListBean bean = new SimpleSelectListBean();
                    bean.setValue(u.getId());
                    bean.setLabel(u.getRealName());
                    entourageIds.add(bean);
                });
            }
        }
        vo.setEntourageIds(entourageIds);
        //任务开始时间
        vo.setTaskStratTime(new Date(taskAssign.getTaskStartTime() * 1000));
        Optional<MhMajorHazard> omh = mhMajorHazardDao.findById(task.getMhId());
        if (omh.isPresent()) {
            vo.setMhName(omh.get().getName());
        }
        vo.setItemList(getTaskItemDataList(task.getPlanId(), task.getId(), taskAssign.getDoneStatus()));
        return vo;
    }

    private List<MhMajorHazardPlanItemVO> getTaskItemDataList(Long planId, Long taskId, Integer doneStatus) {
        //指标
        List<MhMajorHazardPlanItemVO> itemList = new ArrayList<>();
        if (doneStatus == 0) {
            //任务未做的 可能会有多个任务,其中一个任务做了并把计划删除 其他未做的人也要看到检查项
            itemList = mhMajorHazardPlanItemService.findByPlanId(planId, DeleteFlag.EFFECTIVE.getCode());
            for (int i = 0; i < itemList.size(); i++) {
                MhMajorHazardPlanItemVO itemVo = itemList.get(i);
                itemVo.setAppItemName("检查项" + DigitalConversionUtil.arabicNumToChineseNum(i + 1));
            }
        } else {
            //任务已做的
            itemList = mhMajorHazardTaskRecordItemService.findHistoryTaskItemsByTaskId(taskId);
        }
        return itemList;
    }

    @Override
    public void deleteByAssignId(Long assignId) {
        mhMajorHazardTaskAssignService.deleteById(assignId);
    }

    //履职
    @Override
    public MhMajorHazardTaskRecord finishTask(MhMajorHazardTaskRecordDTO recordDto) {
        return mhMajorHazardTaskRecordService.finishTask(recordDto);
    }

    @Override
    public List<WeekRange> getWeekRangeOnMonth(int year, int month) {
        return TaskCycleUtil.getWeekRangeOnMonth(year, month);
    }

    @Override
    public void produceTaskByPlan(MhMajorHazardPlan plan, boolean isNew) {
        List<String> existTaskCycle = null;
        // 计划分配人
        List<Long> executors = parseAssign(plan.getCheckUserIds());
        // 修改计划时，旧任务处理
        if (!isNew) {
            List<MhMajorHazardTask> existTask = removeUnFinishTask(plan.getId());
            if (CollectionUtil.isNotEmpty(existTask)) {
                SimpleDateFormat dateFormatter = SimpleDateFormatCache.getYmd();
                existTaskCycle = existTask.stream().map(t -> dateFormatter.format(t.getStartTime())).collect(Collectors.toList());
            }
            changeAssign(executors, existTask, plan);
        }
        if (null == plan.getCycleType()) {
            throw new RuntimeException("unspecified cycle type");
        }
        PlanCycleType cycleType = PlanCycleType.find(plan.getCycleType());

        List<MhMajorHazardTask> taskList = new ArrayList<>();
        // 区分周、季
        switch (cycleType) {
            case WEEK:
                generate4Week(plan, existTaskCycle, taskList);
                break;
            case SEASON:
                generate4Season(plan, existTaskCycle, taskList);
                break;
            case MONTH:
                generate4Month(plan, existTaskCycle, taskList);
                break;
            case HALF_YEAR:
                generate4HalfYear(plan, existTaskCycle, taskList);
                break;
            default:
                throw new RuntimeException("not support cycle type:" + plan.getCycleType());
        }
        List<MhMajorHazardTaskAssign> assignList = new ArrayList<>();
        // 保存数据、生成assign及通知
        if (CollectionUtil.isNotEmpty(taskList) && CollectionUtil.isNotEmpty(executors)) {
            saveBatch(taskList);
            generateAssigns(taskList, executors, assignList);
            if (CollectionUtil.isNotEmpty(assignList)) {
                assignService.saveBatch(assignList);
                batchSaveNotify(assignList, plan );
            }

        }


    }

    /**
     * 生成任务分配人
     *
     * @param taskList
     * @param executors
     * @param assignList
     */
    private void generateAssigns(List<MhMajorHazardTask> taskList, Collection<Long> executors, List<MhMajorHazardTaskAssign> assignList) {
        taskList.forEach(task -> {
            executors.forEach(executor -> {
                MhMajorHazardTaskAssign assign = new MhMajorHazardTaskAssign()
                        .setTaskId(task.getId())
                        .setAssignUser(executor)
                        .setTaskStartTime(task.getStartTime().getTime() / 1000)
                        .setTaskEndTime(task.getEndTime().getTime() / 1000)
                        .setDoneStatus(TaskStatus.UNFINISH.getStatus());
                assign.setOrgCode(task.getOrgCode());
                assign.setTenentId(task.getTenentId());
                assignList.add(assign);
            });
        });
    }

    @Override
    public void updateTaskStatus(Long taskId) {
        MhMajorHazardTask task = getById(taskId);
        //有一人完成任务就将task的未完成状态修改成已完成
        if (task != null && task.getStatus().equals(TaskStatus.UNFINISH.getStatus())) {
            task.setStatus(TaskStatus.FINISH.getStatus());
            saveOrUpdate(task);
        }
    }

    @Override
    public void deleteUnFinishedTask(Long planId) {
        List<MhMajorHazardTask> tasks = mhMajorHazardTaskMapper.findUnFinishedTasksByPlanId(planId);
        if (!CollectionUtils.isEmpty(tasks)) {
            List<Long> taskIds = tasks.stream().map(e -> e.getId()).collect(Collectors.toList());
            mhMajorHazardTaskMapper.deleteTaskByIds(taskIds);
            //删除个人任务数据
            assignMapper.deleteByTaskIds(taskIds);
        }
    }

    /**
     * 删除计划下已生成但是未做的任务及相关分配人
     * 并返回已做的任务数据
     *
     * @param planId
     */
    private List<MhMajorHazardTask> removeUnFinishTask(Long planId) {
        List<MhMajorHazardTask> tasks = mhMajorHazardTaskMapper.selectTaskByPlanId(planId);
        // 按完成状态分组，未完成（0）的一组，已完成（1）的一组
        Map<Integer, List<MhMajorHazardTask>> taskGroup = tasks.stream().collect(Collectors.groupingBy(MhMajorHazardTask::getStatus));
        List<MhMajorHazardTask> unFinishTask = taskGroup.get(TaskStatus.UNFINISH.getStatus());
        if (CollectionUtil.isNotEmpty(unFinishTask)) {
            List<Long> taskIds = unFinishTask.stream().map(MhMajorHazardTask::getId).collect(Collectors.toList());
            List<MhMajorHazardTaskAssign> assignList = mhMajorHazardTaskAssignMapper.findByTaskIds(taskIds ,TaskStatus.UNFINISH.getStatus() );
            // TODO 删除通知提醒
            if(CollectionUtils.isNotEmpty(assignList)){
                List<Long> assignIdList = assignList.stream().map(MhMajorHazardTaskAssign::getId).collect(Collectors.toList());
                commonIndexNotifyDao.deleteByTaskIds(assignIdList,AppBusinessNoticeTypes.GRS.getType());
            }
            mhMajorHazardTaskMapper.deleteTaskByIds(taskIds);
            assignMapper.deleteByTaskIds(taskIds);

        }
        return taskGroup.get(TaskStatus.FINISH.getStatus());
    }

    /**
     * 周任务生成
     *
     * @param plan
     * @param existTaskCycle
     * @param taskList
     */
    private void generate4Week(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        if (null == plan.getLoopType()) {
            throw new RuntimeException("unspecified loop type");
        }

        PlanLoopType loopType = PlanLoopType.find(plan.getLoopType());

        switch (loopType) {
            case SINGLE:
                generate4SingleWeek(plan, existTaskCycle, taskList);
                break;
            case LOOP:
                generate4LoopWeek(plan, existTaskCycle, taskList);
                break;
            default:
                throw new RuntimeException("not support loop type:" + plan.getLoopType());
        }

    }

    /**
     * 季度任务生成
     *
     * @param plan
     * @param existTaskCycle
     * @param taskList
     */
    private void generate4Season(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        if (null == plan.getLoopType()) {
            throw new RuntimeException("unspecified loop type");
        }

        PlanLoopType loopType = PlanLoopType.find(plan.getLoopType());

        switch (loopType) {
            case SINGLE:
                generate4SingleSeason(plan, existTaskCycle, taskList);
                break;
            case LOOP:
                generate4LoopSeason(plan, existTaskCycle, taskList);
                break;
            default:
                throw new RuntimeException("not support loop type:" + plan.getLoopType());
        }
    }

    /**
     * 月度任务生成
     */
    private void generate4Month(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        if (null == plan.getLoopType()) {
            throw new RuntimeException("unspecified loop type");
        }

        PlanLoopType loopType = PlanLoopType.find(plan.getLoopType());

        switch (loopType) {
            case SINGLE:
                generate4SingleMonth(plan, existTaskCycle, taskList);
                break;
            case LOOP:
                generate4LoopMonth(plan, existTaskCycle, taskList);
                break;
            default:
                throw new RuntimeException("not support loop type:" + plan.getLoopType());
        }
    }

    /**
     * 半年度任务生成
     */
    private void generate4HalfYear(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        if (null == plan.getLoopType()) {
            throw new RuntimeException("unspecified loop type");
        }

        PlanLoopType loopType = PlanLoopType.find(plan.getLoopType());

        switch (loopType) {
            case SINGLE:
                generate4SingleHalfYear(plan, existTaskCycle, taskList);
                break;
            case LOOP:
                generate4LoopHalfYear(plan, existTaskCycle, taskList);
                break;
            default:
                throw new RuntimeException("not support loop type:" + plan.getLoopType());
        }
    }

    private void generate4LoopHalfYear(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        // 选中的年
        String yearStr = plan.getCycleData();
        List<Integer> years = JsonUtil.fromListJson(yearStr, Integer.class);

        if (CollectionUtil.isEmpty(years)) {
            log.info("半年度循环，未指定年,planId:{}", plan.getId());
            return;
        }

        for (Integer year : years) {
            List<Date> allHalfYearStart = TaskCycleUtil.getAllHalfYearStart(year);
            int period = 1;
            for (Date startTime : allHalfYearStart) {
                if (currCycleExitsTask(startTime, existTaskCycle)) {
                    period++;
                    continue;
                }
                DateTime endTime = DateUtil.offsetMonth(startTime, 6);
                String periodDesc = period == 1 ? "上半年" : "下半年";
                String deadLineDesc = String.format(TaskDeadLineDesc.HALF_YEAR_DEAD_LINE, year, periodDesc);
                doGenerate(plan, startTime, endTime, deadLineDesc, taskList);
                period++;
            }
        }
    }

    private void generate4SingleHalfYear(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        // 选中的年份
        Integer year = plan.getCycleYear();
        // 选中的上下半年
        String periodStr = plan.getCycleData();
        List<Integer> periods = JsonUtil.fromListJson(periodStr, Integer.class);
        if (CollectionUtil.isEmpty(periods)) {
            log.info("半年度单次，未指定上下半年,planId:{}", plan.getId());
            return;
        }
        // 半年度-单次只会选中上半年或下半年
        Integer period = periods.get(0);
        // 获取开始时间
        Date startTime = TaskCycleUtil.getHalfYearInYear(year, period);
        if (currCycleExitsTask(startTime, existTaskCycle)) {
            return;
        }

        Date endTime = DateUtil.offsetMonth(startTime, 6);
        String periodDesc = period == 1 ? "上半年" : "下半年";
        String deadLineDesc = String.format(TaskDeadLineDesc.HALF_YEAR_DEAD_LINE, year, periodDesc);
        doGenerate(plan, startTime, endTime, deadLineDesc, taskList);
    }

    private void generate4LoopMonth(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        // 选中的年
        String yearStr = plan.getCycleData();
        List<Integer> years = JsonUtil.fromListJson(yearStr, Integer.class);

        if (CollectionUtil.isEmpty(years)) {
            log.info("月循环，未指定年,planId:{}", plan.getId());
            return;
        }

        for (Integer year : years) {
            List<Date> allMonthStart = TaskCycleUtil.getAllMonthStart(year);
            int month = 1;
            for (Date startTime : allMonthStart) {
                if (currCycleExitsTask(startTime, existTaskCycle)) {
                    month++;
                    continue;
                }
                DateTime endTime = DateUtil.offsetMonth(startTime, 1);
                String deadLineDesc = String.format(TaskDeadLineDesc.MONTH_DEAD_LINE, year, month);
                doGenerate(plan, startTime, endTime, deadLineDesc, taskList);
                month++;
            }
        }
    }

    private void generate4SingleMonth(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        // 选中的年份
        Integer year = plan.getCycleYear();
        // 选中的月
        String monthStr = plan.getCycleData();
        List<Integer> months = JsonUtil.fromListJson(monthStr, Integer.class);
        if (CollectionUtil.isEmpty(months)) {
            log.info("月单次，未指定月,planId:{}", plan.getId());
            return;
        }
        // 月-单次只会选中一个月
        Integer month = months.get(0);
        // 获取开始时间
        Date startTime = TaskCycleUtil.getMonthInYear(year, month);
        if (currCycleExitsTask(startTime, existTaskCycle)) {
            return;
        }

        Date endTime = DateUtil.offsetMonth(startTime, 1);
        String deadLineDesc = String.format(TaskDeadLineDesc.MONTH_DEAD_LINE, year, month);
        doGenerate(plan, startTime, endTime, deadLineDesc, taskList);
    }


    /**
     * 周单次
     *
     * @param plan
     * @param existTaskCycle
     * @param taskList
     */
    private void generate4SingleWeek(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        // 选中的年份和月份
        Integer year = plan.getCycleYear();
        Integer month = plan.getCycleMonth();
        // 选中的周
        String weekStr = plan.getCycleData();
        List<Integer> weeks = JsonUtil.fromListJson(weekStr, Integer.class);
        if (CollectionUtil.isEmpty(weeks)) {
            log.info("周单次，未指定周,planId:{}", plan.getId());
            return;
        }
        // 周-单次只会选中一个周
        Integer week = weeks.get(0);
        // 年、月、第几周 确定任务开始时间
        Date startTime = TaskCycleUtil.getWeekOnMonth(year, month, week);
        // 本周期已有任务，不生成
        if (currCycleExitsTask(startTime, existTaskCycle)) {
            return;
        }
        // 周任务结束时间为开始时间+7天
        DateTime endTime = DateUtil.offsetDay(startTime, 7);
        String deadLineDesc = String.format(TaskDeadLineDesc.WEEK_DEAD_LINE, year, month, week);
        doGenerate(plan, startTime, endTime, deadLineDesc, taskList);
    }

    /**
     * 周循环
     *
     * @param plan
     * @param existTaskCycle
     * @param taskList
     */
    private void generate4LoopWeek(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        // 选中的年份
        Integer year = plan.getCycleYear();
        // 选中的月
        String monthListStr = plan.getCycleData();
        List<Integer> months = JsonUtil.fromListJson(monthListStr, Integer.class);
        if (CollectionUtil.isEmpty(months)) {
            log.info("周循环，未指定月,planId:{}", plan.getId());
            return;
        }
        for (Integer month : months) {
            // 获取当月的所有星期一
            List<Date> allMonday = TaskCycleUtil.getAllMonday(year, month);
            // 每个周一作为每条任务的开始时间
            int week = 1;
            for (Date startTime : allMonday) {
                if (currCycleExitsTask(startTime, existTaskCycle)) {
                    week++;
                    continue;
                }
                DateTime endTime = DateUtil.offsetDay(startTime, 7);
                String deadLineDesc = String.format(TaskDeadLineDesc.WEEK_DEAD_LINE, year, month, week);
                doGenerate(plan, startTime, endTime, deadLineDesc, taskList);
                week++;
            }
        }

    }

    /**
     * 季-单次
     *
     * @param plan
     * @param existTaskCycle
     * @param taskList
     */
    private void generate4SingleSeason(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        // 选中的年份
        Integer year = plan.getCycleYear();
        // 选中的季
        String seasonStr = plan.getCycleData();
        List<Integer> seasons = JsonUtil.fromListJson(seasonStr, Integer.class);
        if (CollectionUtil.isEmpty(seasons)) {
            log.info("季单次，未指定季,planId:{}", plan.getId());
            return;
        }
        // 季-单次只会选中一个季
        Integer season = seasons.get(0);
        // 获取开始时间
        Date startTime = TaskCycleUtil.getSeasonInYear(year, season);
        if (currCycleExitsTask(startTime, existTaskCycle)) {
            return;
        }

        Date endTime = DateUtil.offsetMonth(startTime, 3);
        String deadLineDesc = String.format(TaskDeadLineDesc.SEASON_DEAD_LINE, year, season);
        doGenerate(plan, startTime, endTime, deadLineDesc, taskList);
    }

    /**
     * 更改计划，对已生成且已做的任务-修改分配人
     * @param executors 修改后分配人
     * @param existTask 已存在且已做的任务列表
     */
    private void changeAssign(List<Long> executors, List<MhMajorHazardTask> existTask, MhMajorHazardPlan plan) {
        if (CollectionUtil.isEmpty(existTask)) {
            return;
        }
        // 任务相关数据
        Map<Long, MhMajorHazardTask> taskMap = existTask.stream()
                .collect(Collectors.toMap(MhMajorHazardTask::getId, t -> t));
        List<Long> taskIds = existTask.stream().map(MhMajorHazardTask::getId).collect(Collectors.toList());
        // 当前所有任务分配人
        List<SimpleIdMapping> currentAssign = assignMapper.findUserByTaskIds(taskIds);
        Map<Long, List<SimpleIdMapping>> taskAssignMap = currentAssign.stream()
                .collect(Collectors.groupingBy(SimpleIdMapping::getSourceId));

        taskAssignMap.forEach( (taskId, assignList) -> {
            List<Long> userIds = assignList.stream().map(SimpleIdMapping::getTargetId).collect(Collectors.toList());
            // 当前任务分配人 和 更改后的任务分配人 的 并集
            Collection<Long> union = CollUtil.union(executors, userIds);
            // union 与 原分配人的 差集 即: 此次修改时'新增'的分配人
            Collection<Long> addUser = CollUtil.subtract(union, userIds);
            // union 与 修改后分配人的 差集 即：此次修改时'删除'的分配人
            Collection<Long> removeUser = CollUtil.subtract(union, executors);
            if (CollectionUtil.isNotEmpty(removeUser)) {
                assignMapper.deleteByTaskAndUsers(taskId, removeUser);
            }
            ArrayList<MhMajorHazardTaskAssign> assigns = new ArrayList<>();
            generateAssigns(Collections.singletonList(taskMap.get(taskId)), addUser, assigns);
            if (CollectionUtil.isNotEmpty(assigns)) {
                assignService.saveBatch(assigns);
                batchSaveNotify(assigns, plan);
            }

        });
    }

    private void batchSaveNotify(List<MhMajorHazardTaskAssign> assignList, MhMajorHazardPlan plan ) {
        List<CommonIndexNotify> notifyList = new ArrayList<>();
        List<Long> taskIds = assignList.stream().map(MhMajorHazardTaskAssign::getTaskId).collect(Collectors.toList());
        List<MhMajorHazardTask> taskList = mhMajorHazardTaskMapper.findByIdList(taskIds);
        Map<Long, String> taskMap = taskList.stream().collect(Collectors.toMap(e -> e.getId(), MhMajorHazardTask::getDeadLineDesc));
        assignList.forEach(e -> {
            CommonIndexNotify commonIndexNotify = new CommonIndexNotify();
            Date taskDate = new Date(e.getTaskStartTime() * 1000);
            // 任务已开始, 则无需提醒。
            if (taskDate.after(new Date())) {
                Integer reminderDays = plan.getReminderDays();
                taskDate = DateUtil.offsetDay(taskDate, -reminderDays);
                String msg = "";
                if (Integer.valueOf(0).equals(plan.getCycleType())) {
                    msg = String.format(NotifyContents.GRS_WEEK_WILL_TIME_OUT, taskMap.get(e.getTaskId()), plan.getPlanName());
                } else if (Integer.valueOf(1).equals(plan.getCycleType())) {
                    msg = String.format(NotifyContents.GRS_QUARTER_WILL_TIME_OUT, taskMap.get(e.getTaskId()), plan.getPlanName());
                } else if (Integer.valueOf(2).equals(plan.getCycleType())) {
                    msg = String.format(NotifyContents.GRS_MONTH_WILL_TIME_OUT, taskMap.get(e.getTaskId()), plan.getPlanName());
                } else if (Integer.valueOf(3).equals(plan.getCycleType())) {
                    msg = String.format(NotifyContents.GRS_HALF_YEAR_WILL_TIME_OUT, taskMap.get(e.getTaskId()), plan.getPlanName());
                }

                commonIndexNotify.setUserId(e.getAssignUser())
                        .setType(AppBusinessNoticeTypes.GRS.getType())
                        .setRemindDateTime(DateTimeUtil.date2LocalDateTime(taskDate))
                        .setRemindDate(DateTimeUtil.date2LocalDate(taskDate))
                        .setEduCertUserId(e.getId())
                        .setRemindedOrNot(Constant.REMINDED)
                        .setNotifyMessage(msg)
                        .setOrgCode(e.getOrgCode())
                        .setTenentId(e.getTenentId());
                notifyList.add(commonIndexNotify);
            }
        });
        indexNotifyService.saveList(notifyList);
    }

    /**
     * 季-循环
     *
     * @param plan
     * @param existTaskCycle
     * @param taskList
     */
    private void generate4LoopSeason(MhMajorHazardPlan plan, List<String> existTaskCycle, List<MhMajorHazardTask> taskList) {
        // 选中的年
        String yearStr = plan.getCycleData();
        List<Integer> years = JsonUtil.fromListJson(yearStr, Integer.class);

        if (CollectionUtil.isEmpty(years)) {
            log.info("季循环，未指定年,planId:{}", plan.getId());
            return;
        }

        for (Integer year : years) {
            List<Date> allSeasonStart = TaskCycleUtil.getAllSeasonStart(year);
            // 每个周一作为每条任务的开始时间
            int season = 1;
            for (Date startTime : allSeasonStart) {
                if (currCycleExitsTask(startTime, existTaskCycle)) {
                    season++;
                    continue;
                }
                DateTime endTime = DateUtil.offsetMonth(startTime, 3);
                String deadLineDesc = String.format(TaskDeadLineDesc.SEASON_DEAD_LINE, year, season);
                doGenerate(plan, startTime, endTime, deadLineDesc, taskList);
                season++;
            }
        }

    }


    /**
     * 公用
     *
     * @param plan
     * @param startTime
     * @param endTime
     * @param deadLineDesc
     * @param taskList
     */
    private void doGenerate(MhMajorHazardPlan plan, Date startTime, Date endTime, String deadLineDesc, List<MhMajorHazardTask> taskList) {
        if (endTime.before(new Date())) {
            return;
        }
        MhMajorHazardTask task = new MhMajorHazardTask()
                .setPlanId(plan.getId())
                .setStartTime(startTime)
                .setEndTime(endTime)
                .setTaskName(plan.getPlanName())
                .setCycleType(plan.getCycleType())
                .setCheckUserType(plan.getCheckUserType())
                .setDeadLineDesc(deadLineDesc)
                .setStatus(TaskStatus.UNFINISH.getStatus())
                .setLoopType(plan.getLoopType())
                .setMhId(plan.getMajorHazardId())
                .setFormulateDepartId(plan.getFormulateDepartId())
                .setRemark(plan.getRemark())
                .setEntourageIds(plan.getEntourageIds());
        task.setTenentId(plan.getTenentId());
        task.setOrgCode(plan.getOrgCode());
        taskList.add(task);
    }

    /**
     * 解析任务分配人
     *
     * @param assignStr
     * @return
     */
    private List<Long> parseAssign(String assignStr) {
        if (StringUtils.isBlank(assignStr)) {
            return null;
        }
        return Splitter.on(Constant.SPLIT_COMMA).trimResults().splitToList(assignStr).stream().map(Long::valueOf).collect(Collectors.toList());
    }

    /**
     * 当前周期是否已存在任务
     *
     * @param startTime
     * @param existTaskCycle
     * @return
     */
    private boolean currCycleExitsTask(Date startTime, List<String> existTaskCycle) {
        if (CollectionUtil.isEmpty(existTaskCycle)) {
            return false;
        }
        SimpleDateFormat dateFormatter = SimpleDateFormatCache.getYmd();
        String startTimeStr = dateFormatter.format(startTime);
        return existTaskCycle.contains(startTimeStr);
    }


    @Scheduled(cron = "0 0 1 * * ?")
    private void OverdueTasksSchedule() {
        doOverdueTasksSchedule();
    }

    //定时删选出过期任务
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doOverdueTasksSchedule() {
        Date date = new Date();
        //未检-任务都未做的
        allOverdueTasksSchedule(date);
        //未检-任务有人做了,将未做的人生成未检记录
        partOverdueTasksSchedule(date);
    }

    private void partOverdueTasksSchedule(Date date) {
        long time = date.getTime() / 1000;
        //查询任务已完成但是个人任务还未做的的数据
        List<MhMajorHazardTaskAssignBO> assignBoList = mhMajorHazardTaskAssignMapper.findByEndtimeAndStatus(time, TaskStatus.UNFINISH.getStatus(), TaskStatus.FINISH.getStatus());
        if (CollectionUtils.isEmpty(assignBoList)) {
            return;
        }
        //修改个人记录表状态
        List<Long> assignIds = assignBoList.stream().map(MhMajorHazardTaskAssignBO::getAssignId).distinct().collect(Collectors.toList());
        List<MhMajorHazardTaskAssign> taskAssigns = mhMajorHazardTaskAssignMapper.findByIds(assignIds);
        taskAssigns.forEach(t -> {
            t.setDoneStatus(TaskStatus.OVERDUE.getStatus());
            t.setUpdateTime(date);
        });
        mhMajorHazardTaskAssignService.updateBatchById(taskAssigns);
        //保存记录 记录检查项 个人记录
        mhMajorHazardTaskRecordService.batchSavePartOverdueRecords(assignBoList);
    }

    private void allOverdueTasksSchedule(Date date) {
        List<MhMajorHazardTask> tasks = mhMajorHazardTaskMapper.findAllUnFinishedTasks(date);
        if (CollectionUtils.isEmpty(tasks)) {
            return;
        }
        List<Long> planIds = tasks.stream().map(MhMajorHazardTask::getPlanId).distinct().collect(Collectors.toList());
        //查询逾期任务 计划下的检查项
        List<MhMajorHazardPlanItem> itemList = mhMajorHazardPlanItemMapper.findByPlanIds(planIds);
        if (CollectionUtils.isEmpty(itemList)) {
            return;
        }
        //1.修改任务状态
        tasks.forEach(task -> {
            //状态设置为逾期
            task.setStatus(TaskStatus.OVERDUE.getStatus());
        });
        saveOrUpdateBatch(tasks);
        List<Long> taskIds = tasks.stream().map(MhMajorHazardTask::getId).collect(Collectors.toList());
        //2.修改个人任务状态-逾期
        List<MhMajorHazardTaskAssign> taskAssigns = mhMajorHazardTaskAssignService.batchSaveOverdueTaskAssigen(taskIds);
        //3.生成逾期的记录 任务与记录是一对多的关系 但是逾期的task与record是一对一的关系
        List<MhMajorHazardTaskRecord> records = mhMajorHazardTaskRecordService.batchSaveAllOverdueRecords(tasks, date);
        //4.生成逾期的检查项记录 记录与检查项记录是一对多的关系
        //key:recordId value taskId
        Map<Long, Long> recordTaskIdMap = records.stream().collect(Collectors.toMap(MhMajorHazardTaskRecord::getId, MhMajorHazardTaskRecord::getTaskId));
        //key:taskId value planId
        Map<Long, Long> taskPlanIdMap = tasks.stream().collect(Collectors.toMap(MhMajorHazardTask::getId, MhMajorHazardTask::getPlanId));
        Map<Long, List<MhMajorHazardPlanItem>> planItemMap = itemList.stream().collect(Collectors.groupingBy(e -> e.getPlanId()));
        mhMajorHazardTaskRecordItemService.batchSaveOverdueRecordItems(records, recordTaskIdMap, taskPlanIdMap, planItemMap, date);
        //5.生成逾期的个人记录 记录与个人记录是一对一的关系 但是逾期的记录与个人记录是一对多的关系
        Map<Long, List<MhMajorHazardTaskAssign>> taskAssignMap = taskAssigns.stream().collect(Collectors.groupingBy(MhMajorHazardTaskAssign::getTaskId));
        mhMajorHazardTaskRecordAssignService.batchSaveOverdueRecordAssigns(records, recordTaskIdMap, taskAssignMap, date);
    }

}
